#include <gtest/gtest.h>

#include "NetFrameImplFactory.h"
#include "Interface.h"

#include "Channel.h"
#include "ChannelHandlerAdapter.h"
#include "Client.h"
#include "Service.h"

#include "AsioImpl.h"
#include "Buffer.h"

using namespace frame;

TEST(AsioImplTest, structure){
    auto factory = NetFrameImplFactory::instance().create(ASIO);

    const char * remoteIp = "127.0.0.1";
    int remotePort = 1234;

    IService::Ptr service(new Service(factory));

    IClient::Ptr client(new Client(service, std::make_shared<ChannelHandlerAdapter>()));
    service->add(client);
    service->start();

    IChannel::Ptr channle = client->connect(remoteIp, remotePort);
    service->run();

    service->remove(client->getId());
    ASSERT_EQ(0, service->size());

    service->stop();
}


class ConnectHandlerImplTest : public ConnectHandlerImpl{
public:
    typedef std::shared_ptr<ConnectHandlerImplTest> Ptr;

    ConnectHandlerImplTest(Buffer::Ptr buffer)
    : buffer(buffer)
    {
    }
    ~ConnectHandlerImplTest()override{}

    void connected(std::shared_ptr<ChannelImpl> channelImpl) override{
        assert(channelImpl != nullptr);
        std::cout << "connected:" << std::endl;
        channelImpl->read(buffer);
    }
    void connectFailed(std::shared_ptr<ChannelImpl> channelImpl, int error) override{
        assert(channelImpl != nullptr);
        std::cout << "ConnectFailed:" << error << std::endl;
    }
private:
    Buffer::Ptr buffer;
};


class ChannelHandlerImplTest : public ChannelHandlerImpl {
public:
    typedef std::shared_ptr<ChannelHandlerImplTest> Ptr;
    ChannelHandlerImplTest(Buffer::Ptr buffer)
    : buffer(buffer)
    {}
    ~ChannelHandlerImplTest()override{}


    void disconnected(ChannelImpl::Ptr channel, int error) override{
        std::cout << "ChannelHandlerImplTest::disconnected" << std::endl;
    }

    int write(ChannelImpl::Ptr channel, const char * buf, int size) {
        assert(buf != NULL && size > 0);
        return channel->write(std::make_shared<WriteBuffer>(buf, size));
    }
    int write(ChannelImpl::Ptr channel, const std::string & data){
        return channel->write(std::make_shared<WriteBuffer>(data));
    }
    int write(ChannelImpl::Ptr channel, const char * str){
        assert(str != NULL);
        return channel->write(std::make_shared<WriteBuffer>(str, strlen(str)));
    }

    void write_once(ChannelImpl::Ptr channel){
        const char * str = "Hello";
        buffer->putBytes(str, strlen(str));
        channel->write(buffer); //write read data to nc-end
    }
    void wirte_twice(ChannelImpl::Ptr channel){
        channel->write(buffer); //write read data to nc-end

        const char * str = "Hello";
        channel->write(std::make_shared<WriteBuffer>(str, strlen(str)));
    }
    void read(ChannelImpl::Ptr channel, const char * buf,  std::size_t size) override{
        std::cout << "ChannelHandlerImplTest::read: [" <<  std::string(buf, size) <<  "]" << std::endl;
        std::cout << "read data:" << buffer->toString() << std::endl;

        write_once(channel); // same as write_twice(channel);

        buffer->reset();
        channel->read(buffer);
    }
    void readException(ChannelImpl::Ptr channel, int error) override {
        std::cout << "ChannelHandlerImplTest::readException" << std::endl;
    }

    void writed(ChannelImpl::Ptr channel, std::size_t size) override{
        std::cout << "ChannelHandlerImplTest::writed" << std::endl;
    }
    void writeException(ChannelImpl::Ptr channel, int error) override {
        std::cout << "ChannelHandlerImplTest::writeException" << std::endl;
    }
private:
    frame::Buffer::Ptr buffer;
};


using namespace impl;
using namespace asio;
TEST(AsioImpTest, onlyImp){

    Buffer::Ptr buffer = std::make_shared<Buffer>();
    auto service = std::make_shared<AsioServiceImpl>();
    auto c = std::make_shared<AsioClientImpl>(service, std::make_shared<ConnectHandlerImplTest>(buffer));

    auto channel =  c->connect("127.0.0.1", 1235, std::make_shared<ChannelHandlerImplTest>(buffer));

    service->run();
    service->stop();
}
